package Test.PracticeBrokingQueue;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author handsomePot
 * @date 2021/12/28 22:31
 */
public class MiniArrayBrokingQueue implements BrokingQueue {

    /**
     * 线程并发控制
     */
    private Lock lock = new ReentrantLock();

    /**
     * 当生产者线程 生产数据的时候，它会先检查当前queue是否已经满了，如果已经满了，那么生产者线程就需要挂起
     * 也就是 调用  notFull.await() 进入到notFull的条件队列  等待消费者线程消费数据时唤醒
     */
    private Condition notFull = lock.newCondition();

    /**
     * 消费者线程 消费数据的时候  它也会去检查当前queue是否有数据  如果没有数据  那么消费者线程需要挂起
     * 调用 notEmpty.await() 进入到条件队列中  等待  生产者线程唤醒
     */
    private Condition notEmpty = lock.newCondition();

    /**
     * 底层使用 数组存储元素
     */
    private Object[] queues;

    /**
     * 数组长度
     */
    private int size;

    /**
     * count: 表示当前队列中可以被消费的数据量
     * putptr： 记录生产者 存放数据的下一个位置，每个生产者生产一个数据之后 会让putptr++ 超过数组长度之后归0
     * takeptr:  同上，不过记录的时消费者的逻辑
     */
    private int count, putptr, takeptr;


    public MiniArrayBrokingQueue(int size) {
        this.size = size;
        this.queues = new Object[size];
    }


    @Override
    public void put(Object element) {
        lock.lock();
        try {
            //1、需要判断当前queue是否已经满了
            if (count == size) {
                notFull.await();
            }

            //执行到这里表示  当前队列未满  可以向队列中存放数据
            queues[putptr++] = element;
            count++;

            if (putptr == size) {
                putptr = 0;
            }

            //当向队列成功放入一个元素之后  需要给 notEmpty 一个唤醒信号
            notEmpty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Object take() {
        lock.lock();
        Object result = null;
        try {

            if (count == 0) {
                notEmpty.await();
            }

            result = queues[takeptr++];
            count--;

            if (takeptr == size) {
                takeptr = 0;
            }

            notFull.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return result;
    }
}
